/**
 * @file panel.cpp
 * @author alex, 个人邮箱xueyuankui.good@163.com
 * @brief
 * @version 0.1
 * @date 2024-07-31
 *
 * @copyright Copyright (c) 2024
 *
 */
#include "ash_panel/panel.hpp"

#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <memory.h>

#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <sys/poll.h>
#include <sys/select.h>

struct termios newt, oldt;
// 获取终端相关描述符：标准输入文件描述符STDIN_FILENO=0，标准输出文件描述符=1，标准错误文件描述符=2
int input_dev = STDIN_FILENO; // 键盘输入

//
struct pollfd ufd;

/**
 * @brief ash初始化
 *
 * @return int
 */
int ash_initilize() {
    ufd.fd     = input_dev;
    ufd.events = POLL_IN;
    return 0;
}

/**
 * @brief 无阻塞方式获取用户输入
 * 1. 获取当前终端termios属性，并设置为非ECHO模式（用户的输入不在终端回显）
 * 2. 非阻塞方式读取输入字符
 * 3. 恢复当前终端termios属性
 *
 * @return char
 */
char nonblocking_input(void) {
    int  num;
    char input_ch = '\0';

    tcgetattr(input_dev, &oldt); // 获取终端termios属性
    newt = oldt;

    newt.c_lflag &=
        ~(ICANON | ECHO);                 // Set characters are not buffered(~ICANON) and do not echo(~ECHO).
    tcsetattr(input_dev, TCSANOW, &newt);
    read(input_dev, &input_ch, 1);        // 读取输入字符，不需要回车

    tcsetattr(input_dev, TCSANOW, &oldt); // 重置termios为默认值

    return input_ch;
}

/**
 * @brief 阻塞方式获取用户输入
 * 1. 获取当前终端termios属性，并设置为非ECHO模式（用户的输入不在终端回显）
 * 2. 阻塞方式读取输入字符，并设置超时时间为250ms
 * 3. 恢复当前终端termios属性
 *
 * @return char
 */
char blocking_input(void) {
    int  num;
    char input_ch = '\0';

    tcgetattr(input_dev, &oldt); // 获取终端termios属性
    newt = oldt;

    newt.c_lflag &=
        ~(ICANON | ECHO); // Set characters are not buffered(~ICANON) and do not echo(~ECHO).
    tcsetattr(input_dev, TCSANOW, &newt);
    if ((num = poll(&ufd, 1, 250)) > 0) {
        read(input_dev, &input_ch, 1);    // 读取输入字符，不需要回车
    }
    tcsetattr(input_dev, TCSANOW, &oldt); // 重置termios为默认值

    return input_ch;
}

/**
 * @brief 获取一个被按下的键盘值
 *
 * @return char
 */
char get_char() {
    // char ch = nonblocking_input();
    char ch = blocking_input();
    return ch;
}

/**
 * @brief 解析命令行，生成argv参数列表，返回参数数量argc
 *
 * @param line
 * @param argv
 * @return int
 */
int parse_cmds(char *line, char *argv[]) {
    int argc = 0;

    char *buf = line;
    while ((argv[argc] = strtok(buf, " ")) != NULL) {
        argc++;
        buf = NULL;
    }
    return argc;
}

#ifdef __cplusplus
}
#endif

std::map<std::string, func_cmd> cmd_functions{}; // 存放绑定字符串命令与回调处理函数
std::map<char, func_key>        key_functions{}; // 存放绑定的按键与回调处理函数

/**
 * @brief 绑定命令
 * 绑定用户输入特定字符串与回调函数
 *
 * @param cmd 字符串命令
 * @param fun 回调处理函数
 */
void export_cmd_bind(std::string cmd, func_cmd fun) {
    std::pair<std::string, func_cmd> member{cmd, fun};
    cmd_functions.emplace(member);
}

/**
 * @brief 绑定按键
 * 绑定用户按特定按键与回调函数
 *
 * @param key 按键值
 * @param fun 回调处理函数
 */
void export_key_bind(char key, func_key fun) {
    std::pair<char, func_key> member{key, fun};
    key_functions.emplace(member);
}

/**
 * @brief 遍历命令对应的回调函数并运行
 *
 * @param argc
 * @param argv
 * @return int
 */
int run_functions(int argc, char **argv) {
    std::map<std::string, func_cmd>::iterator it;
    for (it = cmd_functions.begin(); it != cmd_functions.end(); ++it) {
        if (strcmp(argv[0], it->first.c_str()) == 0) {
            it->second(argc, argv);
        }
    }
    return 0;
}

/**
 * @brief 遍历按键对应的回调函数并运行
 *
 * @param key
 * @return int
 */
int run_keys(int key) {
    std::map<char, func_key>::iterator it;
    for (it = key_functions.begin(); it != key_functions.end(); ++it) {
        if (key == it->first) {
            // printf("run %c\n", it->first);
            it->second();
        }
    }
    return 0;
}

/**
 * @brief ash初始化
 *
 * @return int
 */
int ash_init() {
    return ash_initilize();
}

/******************************************************************
 * @brief   运行一次
 * ash获取用户输入并运行相关绑定的函数
 *
 ******************************************************************/
void ash_spin_once() {
    char  line[128] = {};
    char  c;
    int   argc;
    char *argv[16] = {};
    // 获取输入
    c = get_char();

    // 解析输入
    if (c == '\n') {
        printf("[ash]$: ");
        fgets(line, sizeof(line), stdin);  // 注意，使用fgets获取的字符串结尾带有回车
        if (strlen(line) >= 1) {
            line[strlen(line) - 1] = '\0'; // 去掉末尾'\n'并用'\0'代替
        }
        // 解析命令
        argc = parse_cmds(line, argv);
        if (argc <= 0) {
            return;
        }
        // 运行所有注册函数
        run_functions(argc, argv);
    } else {
        // 运行所有绑定键盘按键函数
        run_keys(c);
    }
}

void exit_panel() {
}